home *** CD-ROM | disk | FTP | other *** search
- #: 26702 S2/Beginner's Corner
- 21-Feb-95 14:50:28 Thread=J20
- Sb: #26247-OO vb
- Fm: Kathleen Joeris 75330,156
- To: Pete Washburn 73750,3141
-
- Pete,
-
- Sorry I did not reply sooner; I was busy over the weekend.
-
- (For anyone who wants to look at Pete's article: it is in the Beginner's
- Corner (sect 2) as VBOOP.ZIP.)
-
- Pete: I do not understand one aspect of what you are doing. This has to do
- with creating a new object of an inherited class.
-
- First question is what hOBJ is passed when the NEW_OBJECT is called?
-
- Second question, when does the pipeInstance and WaterpartInstance arrays get
- redimensioned?
-
- Third question, what is the UDT for the pipe class?
-
- Fourth question, is the following description of what happens when a new pipe
- is created correct?
-
- First, the pipeclass is called with a NEW_OBJECT message.
-
- Since this message is not handled by the pipeclass, it is passed to the
- waterpartclass with the NEW_OBJECT message.
-
- I expected this to create a new element in the WaterpartInstances array
- but I did not see a REDIM PRESERVE statement. Did I miss something here?
-
- You then use the self function to call pipeclass, changing the message to
- INIT_OBJECT
-
- INIT_OBJECT is not handled by the pipeclass, so this too is passed to the
- waterpartclass
-
- You then use the self function to call pipeclass, changing the message to
- SET_DIAMETER
-
- The pipeclass calls the WaterPartClass with the message SET_DIAMETER.
- This is the deepest point of the stack, and the stack is
-
- WaterPart (message = SET_DIAMETER)
- Pipe (message = SET_DIAMETER)
- Self (message = SET_DIAMETER)
- WaterPart (message = INIT_OBJECT)
- Pipe (message = INIT_OBJECT)
- Self (message = INIT_OBJECT)
- WaterPart (message = NEW_OBJECT)
- Pipe (message = NEW_OBJECT)
-
- The return value of Pipe(...SET_DIAMETER...) is an empty variant. This
- value is passed back, eventually to the calling routine.
-
- I took a good bit of time with this. I was unable to convince myself that it
- would work as stated. Perhaps you can explain where I missed the boat. Have
- you actually used this code, or was it over simplified when you wrote this?
-
- This seems to fully implement polymorphism, which in my mind is more important
- than inheritance. I really like this aspect of your design.
-
- You and Deborah hide the data within the function/module. I can see the
- benefits of doing this, but do not do it that way. Ultimately the speed of
- the seek is critical to this being practical. As I understand this design, you
- scan arrays eight times to perform a NEW_OBJECT. This shows why I do not do
- it this way.
-
- I believe that your inheritance would work. I think I either misunderstood
- some things, or it needs some fixes to this code, but the underlying theory
- seems entirely sound. I understand why you go through the inheritance stack
- three times, but it is still a bit unwieldly. This would particularly be a
- problem for a deeper inheritance stack.
-
- I am very curious as to how you handle the inheritance of DATA. You do not
- appear to do it in either of the manners I would have tried. One would have
- been
-
- Type PipeClass
- Parent as WaterPartClass
- Length as Double
- EndType
-
- and the other would have been to disallow any access to waterpart data of the
- pipe object except through messages sent to the waterpart class.
-
- THere are, as you mention, a number of things that could be done to make this
- more efficient. However, I think the basic theory is there in what you are
- doing.
-
- Have a good day, Kathleen
-
- =======================================================================
-
- #: 375932 S0/Outbox File
- 22-Feb-95 17:32:00
- Sb: OO vb
- Fm: VBPJFO REP 26702
- To: Kathleen Joeris 75330,156
-
- Hi Kathleen!
-
- Boy, you sure dug in! Good questions all. Let me see if I can clarify them
- somewhat.
-
-
- >First question is what hOBJ is passed when the NEW_OBJECT is called?
-
- I'm passing a Null when I create a new object as in:
-
- manifold1 = Pipe(Null, NEW_OBJECT, "2.5 100")
-
- The first thing NEW_OBJECT does is checks the hObj passed to it. If it is
- Null, then a new object is created (actually the object's handle). The hObj
- is created by ObjectDirectory and returned from the function. You
- then have a global handle to the object, in this example, manifold1
-
-
- >Second question, when does the pipeInstance and WaterpartInstance
- >arrays get redimensioned?
-
- I'm showing my age and experience with QB 2.0, et. al. The days before
- Redim Preserve! Could very well use ReDim Preserve. By the way, how fast
- is it? Of course, one of my other answers will deal with performance issues
- and I'm sure it won't be significant as new objects aren't created all that
- often.
-
- In this example, the arrays are dimensioned during the INIT_CLASS method, as
- in:
-
- a = Pipe(Null, INIT_CLASS, 5)
-
- In this case, I'm not planning on more than 5 instances of this class, so
- the instance arrays are dimmed to 5 elements. No reason that you couldn't
- get rid of the INIT_CLASS method however and just Redim Preserve as
- necessary. Much more dynamic that way too!!
-
- >Third question, what is the UDT for the pipe class?
-
- I'm sorry, I can't recall what "UDT" is.
-
-
- >Fourth question, is the following description of what happens when a new
- >pipe is created correct?
-
- I don't have the actual file handy, but here's what I've got for NEW_OBJECT
- for the pipe class in my code here:
-
- Case NEW_OBJECT
- ' get a new object handle if it hasn't already been created
- If IsNull(hObj) Then
- hObj = ObjectDirectory(Null, NEW_OBJECT, PIPE_CLASS)
- End If
- ' create a new object instance in this class
- pipeCount = pipeCount + 1
- hInst = pipeCount
- objectInstanceHandles(hObj) = hInst
- ' save the object handle in the instance
- pipeInstances(hInst).hObj = hObj
- ' create the ancestor instances
- a = WaterPart(hObj, NEW_OBJECT, value)
- ' initialize the object
- a = Pipe(hObj, INIT_OBJECT, parse(value))
- ' return the object's handle
- Pipe = hObj
-
-
- Here's what's going on. If an object handle hasn't been created by a
- descendant class, then the first thing done is to create one. Next, the
- instance data is created. To speed access to the instance data later, the
- instance's data array index is stored in another array indexed by the
- object's handle. Then, the Pipe class passes the message on to the ancestor
- class, WaterPart in this case, so the WaterPart instances can be created for
- this object. Finally, the object is initialized with INIT_OBJECT.
-
- > First, the pipeclass is called with a NEW_OBJECT message.
-
- Yes
-
- > Since this message is not handled by the pipeclass, it is passed to the
- > waterpartclass with the NEW_OBJECT message.
-
- Yes it is handled by pipeClass and then passed on. Sorry if the file you
- downloaded didn't have that.
-
- > I expected this to create a new element in the WaterPartInstances array
- > but I did not see a REDIM PRESERVE statement. Did I miss something here?
-
- As discussed above, the PipeInstances array was dimmed earlier during
- INIT_CLASS in this example. Could ReDim Preserve.
-
- > You then use the self function to call pipeclass, changing the message to
- > INIT_OBJECT
-
- Yes. Now that all of the instance variables in the ancestor classes have
- been created, its safe to do all of the intializing that needs to be done.
- Here's the code for Pipe INIT_OBJECT:
-
- Case INIT_OBJECT
- Pipe = Self(hObj, SET_LENGTH, value)
-
- For this class, the only thing that has to be done is set the lenght of the
- pipe. Self is used just in case a descendant class has another way of
- processing SET_LENGTH. Note that when the object's handle hObj was created,
- one of the parameters to that call was the class of the object, the
- PIPE_CLASS in this case. When the object is created, the object's class is
- saved so that Self will know where to call to process any methods. So if
- this object was a descendant of Pipe and had another SET_LENGTH method, it
- would be called instead of the Pipe class message. If none of the
- descendant classes process the method, it will eventually get passed back
- here to the Pipe class.
-
-
- > INIT_OBJECT is not handled by the pipeclass, so this too is passed to the
- > waterpartclass
-
- Yes it is. See above. (Sorry if this is different than in the file) Note
- that each class's NEW_OBJECT calls INIT_OBJECT. This allows the instance
- variables for that class to be initialized. So the WaterPart INIT_OBJECT is
- processed before control is returned back to Pipe NEW_OBJECT
-
- (Continued)
- S2
-
-
- #: 375938 S0/Outbox File
- 22-Feb-95 17:38:00
- Sb: OO vb
- Fm: VBPJFO REP 26702
- To: Kathleen Joeris 75330,156
-
- (continued)
-
- > You then use the self function to call pipeclass, changing the message to
- > SET_DIAMETER
-
- > The pipeclass calls the WaterPartClass with the message SET_DIAMETER.
- > This is the deepest point of the stack, and the stack is
-
- > WaterPart (message = SET_DIAMETER)
- > Pipe (message = SET_DIAMETER)
- > Self (message = SET_DIAMETER)
- > WaterPart (message = INIT_OBJECT)
- > Pipe (message = INIT_OBJECT)
- > Self (message = INIT_OBJECT)
- > WaterPart (message = NEW_OBJECT)
- > Pipe (message = NEW_OBJECT)
-
-
- I suspect my code has updated that somewhat. Now, SET_DIAMETER is handled
- during WaterPart's INIT_OBJECT which is called from WaterPart's NEW_OBJECT.
- So the stack would be:
-
- WaterPart (message = SET_DIAMETER)
- Pipe (message = SET_DIAMETER)
- Self (message = SET_DIAMETER)
- WaterPart (message = INIT_OBJECT)
- WaterPart (message = NEW_OBJECT)
- Pipe (message = NEW_OBJECT)
-
- > The return value of Pipe(...SET_DIAMETER...) is an empty variant. This
- > value is passed back, eventually to the calling routine.
-
- In my working code, hObj get's passed back so you have a handle to the newly
- created object. The Pipe function (class) does pass back a variant type so
- that I have the flexiblity to return basically whatever is needed back.
- Before the variant type existed in VB, I returned everything as a string
- which could be then converted into whatever data type was needed. So even
- though I didn't catch on to the ReDim Preserve, I did see Variant as an
- enhancement to this OO thinking!
-
- > I took a good bit of time with this. I was unable to convince myself that
- > it would work as stated. Perhaps you can explain where I missed the boat.
- > Have you actually used this code, or was it over simplified when you wrote
- > this?
-
- The file consist of some messages I made several months ago (almost a year)
- During that time, the design has been enhanced and improved and hopefully
- with what I've added here, will make more sense. There wasn't much interest
- in the thread back then, so I hadn't spent a lot of time keeping it current.
- The code I've shown today is actual code that seems to be doing quite well,
- although I'm always trying to improve things (who isn't!)
-
-
- > This seems to fully implement polymorphism, which in my mind is more
- > important than inheritance. I really like this aspect of your design.
-
- > You and Deborah hide the data within the function/module. I can see the
- > benefits of doing this, but do not do it that way. Ultimately the speed
- > of the seek is critical to this being practical. As I understand this
- > design, you scan arrays eight times to perform a NEW_OBJECT. This shows
- > why I do not do it this way.
-
- > I believe that your inheritance would work. I think I either
- > misunderstood some things, or it needs some fixes to this code, but the
- > underlying theory seems entirely sound. I understand why you go through
- > the inheritance stack three times, but it is still a bit unwieldly.
- > This would particularly be a problem for a deeper inheritance stack.
-
- I think this nearly completely encompasses the three major features of OO
- design. Encapsulation, inheritance, and polymorphism. VB certainly isn't
- optimized for this type of design, but you can meet most of the principles
- this way and receive 95% of the benefits of OO design.
-
- I think the performance issues of any PURE OO design will always sacrifice
- speed for those benefits. It certainly was true with Actor (a Smalltalk
- like language). There sure is alot of message passing going on! I just
- don't see many ways to streamline the process without violating or losing
- some of the OO benefits. If you don't hardcode in the object's class, you
- need to make all those calls to Self to allow any descendant class it's
- chance to process the method. And if you don't pass any unhandled methods
- up to an ancestor, you're not gaining the inheritance benefits. If you
- start opening up the instance data globally, then you've lost the benefits
- of encapsulation.
-
- As always, life's a tradeoff. In my applications, 99.9% of the time the
- computer is waiting for the user to click on something, so the small
- performance penalty is insignificant compared with the easy of designing and
- maintaining the program. If this was a real time control program, then that
- equation would obviously change. Although I've not played with it, I think
- C++ is a reasonable tradeoff between OO principles and performance issues.
- You can take those shortcuts when necessary for performance with the full
- understanding for what you're giving up. With these techniques, I can do
- pretty much the same thing with VB. That wasn't an option with Actor; you
- always had do to things in a pure OO way.
-
- > I am very curious as to how you handle the inheritance of DATA. You do
- > not appear to do it in either of the manners I would have tried. One
- > would have been
-
- > Type PipeClass
- > Parent as WaterPartClass
- > Length as Double
- > EndType
-
- > and the other would have been to disallow any access to waterpart data of
- > the pipe object except through messages sent to the waterpart class.
-
- This is one of the two major limitations of doing this with VB; there isn't
- an easy way to inherit the data from an ancestor class. You could do it by
- including an instance variable of the ancestor's class in the class's
- instance variables as in:
-
- Type pipeObject
- hObj As Integer
- waterPart as WaterPartObject
- length As Integer
- End Type
-
- But then you have the problem of keeping the contents of waterPart the same
- as the instance variables in WaterPart. Plus if you get more than a couple
- of levels deep, it quickly becomes unmanageable. So I had to accept almost
- pure encapsulation in each class, even from it's ancestors. A pain in
- theory, but so far, hasn't been too much of a problem.
-
- The other major limitation was the need to track the class of each object
- and indexing it's instance data in the various classes. This is something
- that is automatic in the other OO languages I've seen.
-
- > There are, as you mention, a number of things that could be done to make
- > this more efficient. However, I think the basic theory is there in what
- > you are doing.
-
- Thanks. It's always nice to be appreciated :) Actually, I'm hopeing to
- gain some suggestions about improving it somewhat. Your suggestion of ReDim
- Preserve fits into that category, thanks.
-
- Pete Washburn,
- W.W. Programming
-
-
- #: 381305 S0/Outbox File
- 26-Feb-95 11:05:00
- Sb: OO vb
- Fm: VBPJFO REP 27047
- To: Kathleen Joeris [SL-1] 75330,156
-
- Hi Kathleen,
-
- UDT - User Defined Type. I knew it looked familiar; just couldn't quite get
- a handle on it! Here's the pipe object:
-
- ' pipe objects
- Type pipeObject
- hObj As Integer
- length As Integer ' in feet
- volume As Integer ' in cubic feet
- change As Integer ' true/false
- End Type
-
-
- Pete Washburn,
- W.W. Programming
-
-